<html>
  <head>
    <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React Native 知识点小记 - Rubin&#39;s Blog</title>
<meta name="description" content="温故而知新" />
<link rel="shortcut icon" href="https://jrb1995.gitee.io/favicon.ico?v=1588066216187">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.10.0/katex.min.css">
<link href="https://cdn.jsdelivr.net/npm/remixicon@1.3.1/fonts/remixicon.css" rel="stylesheet">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Droid+Serif:400,700">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.2/animate.min.css">

<link rel="stylesheet" href="https://jrb1995.gitee.io/styles/main.css">
<link rel="alternate" type="application/atom+xml" title="React Native 知识点小记 - Rubin&#39;s Blog - Atom Feed" href="https://jrb1995.gitee.io/atom.xml">



  </head>
  <body>
    <div id="app" class="main px-4 flex flex-col lg:flex-row">
      <div id="sidebar" class="sidebar-wrapper lg:static lg:w-1/4">
  <div class="lg:sticky top-0">
    <div class="sidebar-content">
      <div class="flex lg:block p-4 lg:px-0 items-center fixed lg:static lg:block top-0 right-0 left-0 bg-white z-50">
        <i class="remixicon-menu-2-line lg:mt-4 text-2xl cursor-pointer animated fadeIn" onclick="openMenu()"></i>
        <a href="https://jrb1995.gitee.io">
          <img class="animated fadeInLeft avatar rounded-lg mx-4 lg:mt-32 lg:mx-0 mt-0 lg:w-24 lg:h-24 w-12 w-12" src="https://jrb1995.gitee.io/images/avatar.png?v=1588066216187" alt="">
        </a>
        <h1 class="animated fadeInLeft lg:text-4xl font-extrabold lg:mt-8 mt-0 text-xl" style="animation-delay: 0.2s">Rubin&#39;s Blog</h1>
      </div>
      
        <div class="animated fadeInLeft" style="animation-delay: 0.4s">
          <p class="my-4 text-gray-600 font-light hidden lg:block">
            文章目录
          </p>
          <div class="toc-container hidden lg:block">
            <ul class="markdownIt-TOC">
<li>
<ul>
<li><a href="#%E5%89%8D%E8%A8%80">前言</a></li>
<li><a href="#%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA">开发环境搭建</a></li>
<li><a href="#%E5%BA%94%E7%94%A8%E8%AE%BE%E7%BD%AE">应用设置</a>
<ul>
<li><a href="#%E4%BF%AE%E6%94%B9%E5%BA%94%E7%94%A8%E5%90%8D%E7%A7%B0">修改应用名称</a></li>
<li><a href="#%E4%BF%AE%E6%94%B9%E5%9B%BE%E6%A0%87">修改图标</a></li>
<li><a href="#%E6%B7%BB%E5%8A%A0%E5%90%AF%E5%8A%A8%E9%A1%B5">添加启动页</a></li>
</ul>
</li>
<li><a href="#%E5%8A%9F%E8%83%BD%E7%BB%84%E4%BB%B6">功能组件</a>
<ul>
<li><a href="#icon-%E7%BB%84%E4%BB%B6">Icon 组件</a></li>
</ul>
</li>
<li><a href="#%E8%B7%AF%E5%BE%84%E5%88%AB%E5%90%8D">路径别名</a></li>
<li><a href="#%E6%89%93%E5%8C%85%E5%92%8C%E5%8F%91%E5%B8%83">打包和发布</a></li>
<li><a href="#%E5%85%B6%E4%BB%96%E9%97%AE%E9%A2%98">其他问题</a></li>
<li><a href="#%E5%85%B6%E4%BB%96%E7%9F%A5%E8%AF%86%E8%B5%84%E6%BA%90">其他知识资源</a></li>
</ul>
</li>
</ul>

          </div>
        </div>
      
    </div>
  </div>
</div>

<div class="menu-container">
  <i class="remixicon-arrow-left-line text-2xl cursor-pointer animated fadeIn close-menu-btn" onclick="closeMenu()"></i>
  <div>
    
      
        <a href="/" class="menu" style="animation-delay: 0s">
          首页
        </a>
      
    
      
        <a href="/archives" class="menu" style="animation-delay: 0.2s">
          归档
        </a>
      
    
      
        <a href="/tags" class="menu" style="animation-delay: 0.4s">
          标签
        </a>
      
    
      
        <a href="/post/about" class="menu" style="animation-delay: 0.6000000000000001s">
          关于
        </a>
      
    
  </div>
  <div class="site-footer">
    <div class="py-4 text-gray-700">Powered by <a href="https://github.com/getgridea/gridea" target="_blank">Gridea</a></div>
    <a class="rss" href="https://jrb1995.gitee.io/atom.xml" target="_blank">RSS</a>
  </div>
</div>
<div class="mask" onclick="closeMenu()">
</div>
      <div class="content-wrapper py-32 lg:p-8 lg:w-3/4 post-detail animated fadeIn">
        <h1 class="text-3xl font-bold lg:mt-16">React Native 知识点小记</h1>
        <div class="text-sm text-gray-700 lg:my-8">
          2019-04-08 / 5 min read
        </div>
        
          <img class="post-feature-image rounded-lg mx-auto my-4" src="https://jrb1995.gitee.io/post-images/react-native.jpg" alt="">
        
        <div class="post-content yue">
          <p>此篇文章用于记录开发中遇到的问题和知识点...</p>
<!-- more -->
<h2 id="前言">前言</h2>
<p>公司打算开发一个App项目。由于没有原生开发人员，组里都是纯前端开发人员，所以技术选型时采用相对成熟的 <code>react-native</code> 来开发。不得不说 <code>react-native</code> 确实很优秀，让前端开发人员能够迅速搭起一个应用运行。但开发过程中也遇到了不少问题，对前端开发来说也是一个不小的挑战。</p>
<h2 id="开发环境搭建">开发环境搭建</h2>
<p>只要严格按照 <a href="https://reactnative.cn/docs/getting-started/">React Native</a> 官方文档搭建即可，当时项目使用 <code>react-native</code> 0.57 版本基础库。</p>
<h2 id="应用设置">应用设置</h2>
<p>应用名称、图标、启动屏相关设置</p>
<h3 id="修改应用名称">修改应用名称</h3>
<ul>
<li>iOS</li>
</ul>
<p>编辑 <code>ios/项目名称/Info.plist</code> 文件：</p>
<pre><code class="language-xml">&lt;key&gt;CFBundleDisplayName&lt;/key&gt;
- &lt;string&gt;$(PRODUCT_NAME)&lt;/string&gt;
+ &lt;string&gt;应用名称&lt;/string&gt;
</code></pre>
<ul>
<li>android</li>
</ul>
<p>编辑 <code>android/app/src/main/res/values/strings.xml</code> 文件：</p>
<pre><code class="language-xml">&lt;resources&gt;
- &lt;string name=&quot;app_name&quot;&gt;default&lt;/string&gt;
+ &lt;string name=&quot;app_name&quot;&gt;应用名称&lt;/string&gt;   
&lt;/resources&gt;
</code></pre>
<h3 id="修改图标">修改图标</h3>
<p>应用图标对尺寸有要求。比较简单地方式是准备一张 <code>1024*1024</code> 的图片，然后使用 <a href="https://icon.wuruihong.com/">图标工厂</a> 在线生成。</p>
<p>生成文件如下：</p>
<figure data-type="image" tabindex="1"><img src="https://jrb1995.gitee.io/post-images/1573218973706.jpg" alt="" loading="lazy"></figure>
<ul>
<li>iOS</li>
</ul>
<p>替换 <code>ios/项目名称/Images.xcassets/AppIcon.appiconset</code> 中的内容。如果不需要全部尺寸可以用 <code>XCode</code> 打开项目 <code>Images.xcassets</code> &gt; <code>AppIcon</code> 拖入相应尺寸的图标。</p>
<ul>
<li>android</li>
</ul>
<p>替换 <code>android/app/src/main/res</code> 下对应的图标。</p>
<h3 id="添加启动页">添加启动页</h3>
<p>应用打开时会有短暂的白屏，为解决这一问题项目中使用 <a href="https://github.com/crazycodeboy/react-native-splash-screen">react-native-splash-screen</a> 库。文档写的很详细。</p>
<p><a href="https://developer.apple.com/design/human-interface-guidelines/ios/icons-and-images/launch-screen/">Launch Screen苹果官方分辨率大全</a></p>
<h2 id="功能组件">功能组件</h2>
<h3 id="icon-组件">Icon 组件</h3>
<p>关于 <code>svg</code> 文件的优点就不必多说了。要想在应用中使用会有一些问题，不像在 web 中直接 img 标签引入就可以用了。比如安卓不支持 svg 静态文件的 require 😂。也为了避免每次打开应用请求多个 svg 浪费资源。</p>
<p>所以可以将多个 svg 文件通过脚本转换成一个 js 对象或者一个 json 文件，其格式如下：</p>
<pre><code class="language-js">export default {
  &quot;arrow-left&quot;: &quot;&lt;svg&gt;...&lt;/svg&gt;&quot;
}
</code></pre>
<p>上面的转换脚本可以参考 <a href="https://github.com/HuJiaoHJ/svg2json">svg2json</a></p>
<p>安装</p>
<pre><code class="language-shell">npm install react-native-svg2json --save-dev
</code></pre>
<p>在项目根目录输入命令如下：</p>
<pre><code class="language-shell">// svg2json -i ${inDir} -o ${outFile}
./node_modules/.bin/svg2json -i ./src/assets/svgs -o ./src/assets/svgs.js
</code></pre>
<p>或者写入 <code>package.json</code> scripts 中，直接运行 <code>npm run svg2json</code></p>
<pre><code class="language-js">&quot;scripts&quot;: {
    &quot;start&quot;: &quot;node node_modules/react-native/local-cli/cli.js start&quot;,
    &quot;test&quot;: &quot;jest&quot;,
    &quot;svg2json&quot;: &quot;node node_modules/.bin/svg2json -i ./src/assets/svgs -o ./src/assets/svgs.js&quot;
}
</code></pre>
<p>之后如何使用，就需要 <a href="https://github.com/vault-development/react-native-svg-uri">react-native-svg-uri</a> 库，支持传入 svg 字符串来渲染 svg。但还需做好组件封装。</p>
<pre><code class="language-js">import React, { Component } from 'react';
import SvgUri from 'react-native-svg-uri';
import svgs from '@/assets/svgs.js'; // 生成的 svg 字符串对象
export default class Icon extends Component {
  render() {
    const { name, color, size, style } = this.props;
    let svgXmlData = svgs[name]
    if (!svgXmlData) {
      let err_msg = `没有&quot;${name}&quot;这个icon文件`;
      throw new Error(err_msg);
    }
    return (
      &lt;SvgUri width={size} height={size} svgXmlData={svgXmlData} fill={color} style={style} /&gt;
    )
  }
}
</code></pre>
<p>使用</p>
<pre><code class="language-js">&lt;Icon name=&quot;ac_unit&quot; size=&quot;40&quot; color=&quot;#ccc&quot;/&gt;
</code></pre>
<h2 id="路径别名">路径别名</h2>
<p>在开发过程中通常使用 <code>../../../</code> 的方式引入组件，这种方式路径过深且不够优雅。</p>
<p>插件安装</p>
<pre><code class="language-shell">npm install babel-plugin-root-import --save-dev
或者
yarn add babel-plugin-root-import --dev
</code></pre>
<p>在 <code>babel</code> 配置文件 <code>.babelrc</code> 或者 <code>babel.config.js</code> 中</p>
<pre><code class="language-js">{
  &quot;plugins&quot;: [
    [&quot;babel-plugin-root-import&quot;, {
      &quot;rootPathPrefix&quot;: &quot;~&quot;,
      &quot;rootPathSuffix&quot;: &quot;src&quot;
    }]
  ]
}
</code></pre>
<blockquote>
<p>注意： 当 rootPathPrefix 设置为 @ 可能会导致一些以 @ 开头的 npm 包引入错误，如 <code>@ant-design/react-native</code></p>
</blockquote>
<p>之后使用</p>
<pre><code class="language-js">import foo from '~/foo';
const bar = require('~/bar');
</code></pre>
<h2 id="打包和发布">打包和发布</h2>
<p>应用开发完就该考虑上架了😘，由于没有经验上网查询了很多资料。</p>
<p>链接：<a href="https://my.oschina.net/KJhulinhua/blog/1585628">react native ios打包流程</a>、<a href="https://www.pgyer.com/doc/view/build_ipa">打包iOS的ipa文件</a>、<a href="https://www.jianshu.com/p/431ed0536754">iOS生成证书 描述文件 打包上架Appstore详细流程</a></p>
<h2 id="其他问题">其他问题</h2>
<p>android 图片不显示，图片名称不能是中文（开发时碰到这个问题😓）。</p>
<h2 id="其他知识资源">其他知识资源</h2>
<p><a href="https://juejin.im/post/5c5402f8f265da2dbd7fd084">https://juejin.im/post/5c5402f8f265da2dbd7fd084</a></p>

        </div>

        
          <a class="animated fadeInUp p-2 items-center text-sm text-gray-700 border hover:bg-gray-300 leading-none rounded-full flex lg:inline-flex m-4 " href="https://jrb1995.gitee.io/tag/9u5AQlh3y/">
            <span class="flex-auto">react</span>
          </a>
        
          <a class="animated fadeInUp p-2 items-center text-sm text-gray-700 border hover:bg-gray-300 leading-none rounded-full flex lg:inline-flex m-4 " href="https://jrb1995.gitee.io/tag/oGDYGXS3n/">
            <span class="flex-auto">react-native</span>
          </a>
        


        <div class="flex justify-between py-8">
          
            <div class="prev-post">
              <a href="https://jrb1995.gitee.io/post/5dc8a231f268da4d40712f8a/">
                <h3 class="post-title">
                  <i class="remixicon-arrow-left-line"></i>
                  CSS开发技巧整理
                </h3>
              </a>
            </div>
          

          
        </div>

        
          
            <link rel="stylesheet" href="https://unpkg.com/gitalk/dist/gitalk.css">
<script src="https://unpkg.com/gitalk/dist/gitalk.min.js"></script>

<div id="gitalk-container"></div>

<script>

  var gitalk = new Gitalk({
    clientID: '58415ac62e821b4b6bbd',
    clientSecret: '69a93aa48149a7a74c6517efbbb32c57bc07799f',
    repo: 'jrb1995.github.io',
    owner: 'jrb1995',
    admin: ['jrb1995'],
    id: (location.pathname).substring(0, 49),      // Ensure uniqueness and length less than 50
    distractionFreeMode: false  // Facebook-like distraction free mode
  })

  gitalk.render('gitalk-container')

</script>

          

          
        

      </div>
    </div>

    <script src="https://jrb1995.gitee.io/media/prism.js"></script>  
<script>

Prism.highlightAll()

let mainNavLinks = document.querySelectorAll(".markdownIt-TOC a");

// This should probably be throttled.
// Especially because it triggers during smooth scrolling.
// https://lodash.com/docs/4.17.10#throttle
// You could do like...
// window.addEventListener("scroll", () => {
//    _.throttle(doThatStuff, 100);
// });
// Only not doing it here to keep this Pen dependency-free.

window.addEventListener("scroll", event => {
  let fromTop = window.scrollY;

  mainNavLinks.forEach((link, index) => {
    let section = document.getElementById(decodeURI(link.hash).substring(1));
    let nextSection = null
    if (mainNavLinks[index + 1]) {
      nextSection = document.getElementById(decodeURI(mainNavLinks[index + 1].hash).substring(1));
    }
    if (section.offsetTop <= fromTop) {
      if (nextSection) {
        if (nextSection.offsetTop > fromTop) {
          link.classList.add("current");
        } else {
          link.classList.remove("current");    
        }
      } else {
        link.classList.add("current");
      }
    } else {
      link.classList.remove("current");
    }
  });
});


document.addEventListener("DOMContentLoaded", function() {
  var lazyImages = [].slice.call(document.querySelectorAll(".post-feature-image.lazy"));

  if ("IntersectionObserver" in window) {
    let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(entry) {
        if (entry.isIntersecting) {
          let lazyImage = entry.target
          lazyImage.style.backgroundImage = `url(${lazyImage.dataset.bg})`
          lazyImage.classList.remove("lazy")
          lazyImageObserver.unobserve(lazyImage)
        }
      });
    });

    lazyImages.forEach(function(lazyImage) {
      lazyImageObserver.observe(lazyImage)
    })
  } else {
    // Possibly fall back to a more compatible method here
  }
});

const menuContainer = document.querySelector('.menu-container')
const menus = document.querySelectorAll('.menu-container .menu')
const mask = document.querySelector('.mask')
const contentWrapper = document.querySelector('.content-wrapper')
const latestArticle = document.querySelector('.latest-article')
const readMore = document.querySelector('.read-more')
const indexPage = document.querySelector('.index-page')

const isHome = location.pathname === '/'
if (latestArticle) {
  latestArticle.style.display = isHome ? 'block' : 'none'
  readMore.style.display = isHome ? 'block' : 'none'
  indexPage.style.display = isHome ? 'none' : 'block'
}

const openMenu = () => {
  menuContainer.classList.add('open')
  menus.forEach(menu => {
    menu.classList.add('animated', 'fadeInLeft')
  })
  mask.classList.add('open')
  contentWrapper.classList.add('is-second')
}

const closeMenu = () => {
  menuContainer.classList.remove('open')
  menus.forEach(menu => {
    menu.classList.remove('animated', 'fadeInLeft')
  })
  mask.classList.remove('open')
  contentWrapper.classList.remove('is-second')
}
</script>
  
  </body>
</html>
